package com.lu.stack.gateway.config;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import lombok.extern.slf4j.Slf4j;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.util.Arrays;
import java.util.stream.Collectors;

@Slf4j
@Configuration
@EnableCaching
public class RedisConfig {

    /**
     * 自定义redisson客户端连接配置
     * @return
     */
    @Bean
    public RedissonClient redisson(SpringUtil springUtil){
        Environment environment = springUtil.getBean(Environment.class);
        if(environment.containsProperty("spring.redis.sentinel.master")){
            return Redisson.create(this.sentinelServersConfig(environment));
        } else {
            return Redisson.create(this.singleServerConfig(environment));
        }
    }

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    public RedisTemplate<Object, Object> redisTemplate(
            RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
        //使用fastjson序列化
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // value值的序列化采用 StringRedisSerializer
        template.setValueSerializer(stringRedisSerializer);
        template.setHashValueSerializer(stringRedisSerializer);
        // key的序列化采用StringRedisSerializer
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());

        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    /**
     * 集群
     * @param environment
     * @return
     */
    private Config sentinelServersConfig(Environment environment) {
        Config config = new Config();
        String master = environment.getProperty("spring.redis.sentinel.master");
        String nodes = environment.getProperty("spring.redis.sentinel.nodes");
        String database = null;
        String password = null;
        if(environment.containsProperty("spring.redis.database")){
            database = environment.getProperty("spring.redis.database");
        }
        if(environment.containsProperty("spring.redis.password")){
            password = environment.getProperty("spring.redis.password");
        }
        String minSize = environment.getProperty("redis.min.connection.size");
        String maxSize = environment.getProperty("redis.max.connection.size");
        if(ObjectUtil.isEmpty(minSize)){
            minSize = "24";
        }
        if(ObjectUtil.isEmpty(maxSize)){
            maxSize = "48";
        }
        SentinelServersConfig sentinelServersConfig = config.useSentinelServers();
        String[] addresses = Arrays.stream(nodes.split(StrUtil.COMMA)).map(address -> {
            String[] tmp = address.split(StrUtil.COLON);
            return StrUtil.format("redis://{}:{}", tmp);
        }).collect(Collectors.toList()).stream().toArray(String[]::new);
        log.info("redissonClient(集群哨兵)：" + StrUtil.join(StrUtil.COMMA, addresses));
        sentinelServersConfig.addSentinelAddress(addresses)//集群
                .setMasterName(master)
                .setMasterConnectionMinimumIdleSize(Integer.valueOf(minSize))//设置对于master节点的连接池中连接数最小为10
                .setMasterConnectionPoolSize(Integer.valueOf(maxSize))//设置对于master节点的连接池中连接数最大为10
                .setIdleConnectionTimeout(10000)//如果当前连接池里的连接数量超过了最小空闲连接数，而同时有连接空闲时间超过了该数值，那么这些连接将会自动被关闭，并从连接池里去掉。时间单位是毫秒。
                .setConnectTimeout(30000)//同任何节点建立连接时的等待超时。时间单位是毫秒。
                .setTimeout(3000);//等待节点回复命令的时间。该时间从命令发送成功时开始计时。;
        if (StrUtil.isNotEmpty(database)) {
            sentinelServersConfig.setDatabase(Integer.valueOf(database));
        }
        if (StrUtil.isNotEmpty(password)) {
            sentinelServersConfig.setPassword(password);
        }
        return config;
    }

    /**
     * 单例
     * @param environment
     * @return
     */
    private Config singleServerConfig(Environment environment) {
        Config config = new Config();
        String host = environment.getProperty("spring.redis.host");
        String port = environment.getProperty("spring.redis.port");
        String database = null;
        String password = null;
        if(environment.containsProperty("spring.redis.database")){
            database = environment.getProperty("spring.redis.database");
        }
        if(environment.containsProperty("spring.redis.password")){
            password = environment.getProperty("spring.redis.password");
        }
        String minSize = environment.getProperty("redis.min.connection.size");
        String maxSize = environment.getProperty("redis.max.connection.size");
        if(ObjectUtil.isEmpty(minSize)){
            minSize = "12";
        }
        if(ObjectUtil.isEmpty(maxSize)){
            maxSize = "24";
        }
        SingleServerConfig singleServerConfig = config.useSingleServer();
        singleServerConfig.setAddress("redis://" + host + ":" + port + "")
                .setConnectionMinimumIdleSize(Integer.valueOf(minSize))//设置对于master节点的连接池中连接数最小为10
                .setConnectionPoolSize(Integer.valueOf(maxSize))//设置对于master节点的连接池中连接数最大为10
                .setIdleConnectionTimeout(10000)//如果当前连接池里的连接数量超过了最小空闲连接数，而同时有连接空闲时间超过了该数值，那么这些连接将会自动被关闭，并从连接池里去掉。时间单位是毫秒。
                .setConnectTimeout(30000)//同任何节点建立连接时的等待超时。时间单位是毫秒。
                .setTimeout(3000);//等待节点回复命令的时间。该时间从命令发送成功时开始计时。
        log.info("redissonClient(单实例)：" + singleServerConfig.getAddress());
        if (StrUtil.isNotEmpty(database)) {
            singleServerConfig.setDatabase(Integer.valueOf(database));
        }
        if (StrUtil.isNotEmpty(password)) {
            singleServerConfig.setPassword(password);
        }
        return config;
    }

}

